﻿/******************************************************
* author :  cwj
* email  :  chenwenji_360@live.com 
* history:  created by cwj 2015/8/6 15:30:29 
* clrversion :4.0.30319.18444
******************************************************/

using Machine.DataAccess.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace System.Linq
{
    /// <summary>
    /// 注入IQueryable的拓展
    /// </summary>
    public static class QueryableExtession
    {
        /// <summary>
        /// 模糊查询
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="query"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static IQueryable<TEntity> Like<TEntity>(this IQueryable<TEntity> query, Expression<Func<TEntity, bool>> predicate)
        {
            var set = query as DbSet<TEntity>;
            if (query == null) throw new ArgumentNullException("类型异常");
            //var expressionTemp = query.Expression;
            //while (expressionTemp.NodeType == ExpressionType.Call)
            //{
            //    expressionTemp = (expressionTemp as MethodCallExpression).Arguments[0];
            //}
            //if (expressionTemp.NodeType == ExpressionType.Constant)
            //{
            //    expressionTemp = (expressionTemp as ConstantExpression).Value as DbSet<TEntity>
            //}
            var callExpression = Expression.Call(typeof(QueryableExtession), "Like", 
                new Type[] { typeof(TEntity) }, 
                query.Expression, Expression.Quote(predicate));
            return new QueryProvider(set.ProviderElement).CreateQuery<TEntity>(callExpression);
        }

        public static IQueryable<TEntity> Or<TEntity>(this IQueryable<TEntity> query, Expression<Func<TEntity, bool>> predicate)
        {
            var set = query as DbSet<TEntity>;
            if (query == null) throw new ArgumentNullException("类型异常");
            //var expressionTemp = query.Expression;
            //while (expressionTemp.NodeType == ExpressionType.Call)
            //{
            //    expressionTemp = (expressionTemp as MethodCallExpression).Arguments[0];
            //}
            //if (expressionTemp.NodeType == ExpressionType.Constant)
            //{
            //    expressionTemp = (expressionTemp as ConstantExpression).Value as DbSet<TEntity>
            //}
            var callExpression = Expression.Call(typeof(QueryableExtession), "Or",
                new Type[] { typeof(TEntity) },
                query.Expression, Expression.Quote(predicate));
            return new QueryProvider(set.ProviderElement).CreateQuery<TEntity>(callExpression);
        }

        /// <summary>
        /// 贪婪加载,对二级表显式加载.默认不加载二级表
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="query"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static IQueryable<TEntity> Include<TEntity, TProperty>(this IQueryable<TEntity> query, Expression<Func<TEntity, TProperty>> predicate)
        {
            var set = query as DbSet<TEntity>;
            if (query == null) throw new ArgumentNullException("类型异常");

            var callExpression = Expression.Call(typeof(QueryableExtession), "Include",
                new Type[] { typeof(TEntity),typeof(TProperty) },
                query.Expression, Expression.Quote(predicate));
            return new QueryProvider(set.ProviderElement).CreateQuery<TEntity>(callExpression);
        }

        /// <summary>
        /// 生成地理位置的对比算式
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="query"></param>
        /// <param name="latPredicate"></param>
        /// <param name="lngPredicate"></param>
        /// <param name="distancePredicate"></param>
        /// <param name="lat"></param>
        /// <param name="lng"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        public static IQueryable<TEntity> GeoDistance<TEntity>(
            this IQueryable<TEntity> query,
            Expression<Func<TEntity, double>> latPredicate,
            Expression<Func<TEntity, double>> lngPredicate,
            Expression<Func<TEntity, double>> distancePredicate,
            double lat, double lng)
        //GeoCompareType type)
        {
            var set = query as DbSet<TEntity>;
            if (query == null) throw new ArgumentNullException("类型异常");

            var callExpression = Expression.Call(typeof(QueryableExtession), "GeoDistance",
                new Type[] { typeof(TEntity) },
                query.Expression,
                Expression.Quote(latPredicate),
                Expression.Quote(lngPredicate),
                Expression.Quote(distancePredicate),
                Expression.Constant(lat),
                Expression.Constant(lng));
            return new QueryProvider(set.ProviderElement).CreateQuery<TEntity>(callExpression);
        }

        public static bool Delete<TEntity>(this IQueryable<TEntity> query, Expression<Func<TEntity, bool>> predicate)
        {
            var set = query as DbSet<TEntity>;
            if (query == null) throw new ArgumentNullException("类型异常");
            var callExpression = Expression.Call(typeof(QueryableExtession), "Delete",
                new Type[] { typeof(TEntity) },
                query.Expression, Expression.Quote(predicate));
            //return new QueryProvider(set.ProviderElement).CreateQuery<TEntity>(callExpression);
            return new QueryProvider(set.ProviderElement).ExecuteNonQuery(callExpression);
        }

        public static bool Update<TEntity>(this IQueryable<TEntity> query, Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, bool>> where)
        {
            var set = query as DbSet<TEntity>;
            if (query == null) throw new ArgumentNullException("类型异常");
            var callExpression = Expression.Call(typeof(QueryableExtession), "Update",
                new Type[] { typeof(TEntity) },
                query.Expression,
                Expression.Quote(predicate),
                Expression.Quote(where));
            //return new QueryProvider(set.ProviderElement).CreateQuery<TEntity>(callExpression);
            return new QueryProvider(set.ProviderElement).ExecuteNonQuery(callExpression);
        }
    }

    //[Flags]
    //public enum GeoCompareType
    //{
    //    /// <summary>
    //    /// 小于
    //    /// </summary>
    //    LessThan,
    //    /// <summary>
    //    /// 等于
    //    /// </summary>
    //    Equal,
    //    /// <summary>
    //    /// 大于
    //    /// </summary>
    //    MoreThan
    //}
}
